#include "socket_gethostbyname.h"
#include "socket_wrapper.h"
#include "socket_inc.h"
#include <core/QbLogger.h>
#include <core/system/tick_util.h>
#include <assert.h>
namespace qb
{
	void hosttable::SetUpdatedTime()
	{
		m_uUpdatedTime = TickTime::MillisecondTimestamp();		
	}
	hosttable CSocketGethostbyname::m_hosttable;
	CSocketGethostbyname::CSocketGethostbyname()
		: m_thread(NULL)
		, m_event(*EventWrapper::Create(false))
		, m_interface_crit(*CriticalSectionWrapper::CreateCriticalSection())
	{
	}

	CSocketGethostbyname::~CSocketGethostbyname()
	{
		CancelGetHostByName();
		delete &m_event;
		delete &m_interface_crit;
	}

	int64_t CSocketGethostbyname::GetMillisecondTimestamp()
	{
		return TickTime::MillisecondTimestamp();
	}

	int32_t CSocketGethostbyname::GetHostByName(const char* host,bool force /*= false*/)
	{		
		if (host == NULL)
		{
			log_error("host name is NULL");
			return -1;
		}
		if (std::string(host).empty())
		{
			log_error("host name is empty");
			return -1;
		}
        {
            CriticalSectionScoped lock(&m_interface_crit);
            if (std::string(host).compare(m_host) == 0 && !IsHostTimeout())
            {
                log_info("%s host is running",host);
                return 0;
            }
            if (!force)
            {
                if (m_hosttable.get_iptable(m_iptable, host) == 0 && !IsHostTimeout())
                {
                    m_host = host;
                    return 0;
                }
            }
        }
				
		if (m_thread)
		{
			log_warning("already run host(%s),ignore target host(%s)",m_host.c_str(),host);
			CancelGetHostByName();
		}

        {
            CriticalSectionScoped lock(&m_interface_crit);
            m_host = host;
            m_iptable.clear();
            m_event.Reset();
			m_hosttable.ClearHtable();
            ++m_run_flag;
            uint32_t threadId = 0;
            m_thread = ThreadWrapper::CreateThread(ThreadFun,this,
                kNormalPriority,"gethostbyname thread");
            m_thread->Start(threadId);
        }		

		return 0;
	}

	int32_t CSocketGethostbyname::CancelGetHostByName()
	{		
		m_run_flag.CompareExchange(0,m_run_flag.Value());
		m_event.Set();
        {
            CriticalSectionScoped lock(&m_interface_crit);
            StopThread(&m_thread);
            m_iptable.clear();
            m_host.clear();
        }        

		return 0;
	}

	int32_t CSocketGethostbyname::WaitProcess(int32_t timeout_ms)
	{
		//CriticalSectionScoped lock(&m_interface_crit);
		if (m_run_flag.Value() == 0)
		{
			return 0;
		}
		m_event.Wait(timeout_ms);
		if (m_run_flag.Value() == 0)
		{
			return 0;
		}
		return -1;
	}

	int32_t CSocketGethostbyname::GetNumOfIPAddress()
	{
		CriticalSectionScoped lock(&m_interface_crit);
		return (int32_t)m_iptable.size();
	}

	const char* CSocketGethostbyname::GetIPAddress(int index)
	{
		CriticalSectionScoped lock(&m_interface_crit);
		if (index >= (int32_t)m_iptable.size())
		{
			return "";
		}
		return m_iptable[index].c_str();
	}

	const char* CSocketGethostbyname::GetFirstIPAddress()
	{
		CriticalSectionScoped lock(&m_interface_crit);
		if (m_iptable.size() == 0)
		{
			return "";
		}

		return m_iptable[0].c_str();
	}

	bool CSocketGethostbyname::ThreadFun(void* arg)
	{
		return static_cast<CSocketGethostbyname*>(arg)->ThreadRun(arg);
	}

	bool CSocketGethostbyname::ThreadRun(void* arg)
	{
		hostent* ent = gethostbyname(m_host.c_str());
		if (m_run_flag.Value() == 0)
		{
			return false;
		}
		if (ent)
		{
			int32_t i = 0;
			while (ent->h_addr_list[i] != 0)
			{
				std::string ipaddress = inet_ntoa(*(struct in_addr *)ent->h_addr_list[i++]);
				m_iptable.push_back(ipaddress);
				log_trace("gethostbyname, host(%s==>%s)", m_host.c_str(),ipaddress.c_str());
			}
		}
		else
		{
			log_error("gethostbyname failed, host(%s), lasterror(%d)", m_host.c_str(), CSocketWrapper::GetSockError());
		}
		if (m_iptable.size() > 0)
		{
			m_hosttable.add(m_iptable, m_host.c_str());
			m_hosttable.SetUpdatedTime();
		}
		m_run_flag.CompareExchange(0,m_run_flag.Value());
		m_event.Set();

		return false;
	}

	void CSocketGethostbyname::StopThread(ThreadWrapper** thread_)
	{
		ThreadWrapper* tmpPtr = *thread_;
		*thread_ = NULL;
		if (tmpPtr)
		{
			tmpPtr->SetNotAlive();

			bool force = false;
			if (tmpPtr->Stop(force))
			{
				delete tmpPtr;
			}
			else
			{
				if (force)
				{
					log_warning("stop thread failed, force terminate success.");
				}
				else
				{
					log_error("stop thread failed, force terminate failed.");

				}
				assert(0);
			}
		}
	}

	bool CSocketGethostbyname::IsHostTimeout()
	{
		bool res = false;
		int64_t uCurrTime = TickTime::MillisecondTimestamp();
		if ( uCurrTime - m_hosttable.GetUpdatedTime() >600000)
		{
			res = true;
		}
		return res;
	}
}
